package com.atguigu.distributed.lock.redis.service;

import com.atguigu.distributed.lock.lock.DistributeLookClientFactory;
import com.atguigu.distributed.lock.redis.lock.DistributedRedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author linjy on 2022/10/10
 */
@Service
public class RedisStockService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DistributeLookClientFactory  distributeLookClientFactory;

    /**
     * redis分布式锁基本实现（已防死锁——设置过期时间，防删除——lua脚本判断是自己的锁才删除）
     */
    public void deduct1() {
        String uuid = UUID.randomUUID().toString();
        // 加锁，获取锁失败重试
        while (!this.stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS)) {
            try {
                Thread.sleep(40);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            //1.查询库存信息
            String stock = this.stringRedisTemplate.opsForValue().get("stock");

            //2.判断库存是否充足
            if (stock != null && stock.length() != 0) {
                int st = Integer.parseInt(stock);
                if (st > 0) {
                    //3.扣减库存
                    this.stringRedisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
        } finally {
            // 先判断是否自己的锁，再解锁
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                    "then " +
                    "   return redis.call('del', KEYS[1]) " +
                    "else " +
                    "   return 0 " +
                    "end";
            this.stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Collections.singletonList("lock"), uuid);
        }

    }

    /**
     * redis分布式锁基本实现（已防死锁——设置过期时间，防删除——每个锁设置自己的标志uuid，可重入——同一线程可重入(利用hash结果记录重入次数)）
     * 可重入有两种
     * 1、可以重入同一线程里方法的同一把锁
     * 2、可以重入同一线程（时间片轮转调度是错开的，所以不会有线程安全问题）
     */
    public void deduct2() {
        DistributedRedisLock redisLock = this.distributeLookClientFactory.getRedisLock("lock");

        redisLock.lock();

        try {
            // 1. 查询库存信息
            String stock = stringRedisTemplate.opsForValue().get("stock").toString();

            // 2. 判断库存是否充足
            if (stock != null && stock.length() != 0) {
                Integer st = Integer.valueOf(stock);
                if (st > 0) {
                    // 3.扣减库存
                    stringRedisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
            //测试可重入
            //test();

        } finally {
            redisLock.unlock();
        }
    }

    /**
     * 测试可重入锁
     */
    public void test(){
        DistributedRedisLock lock = this.distributeLookClientFactory.getRedisLock("lock");
        lock.lock();
        System.out.println("测试可重入锁");
        lock.unlock();
    }

    /**
     * 明明已经结束，却为什么没有自动终止程序，
     * 这是因为系统默认当Timer运行结束后，如果没有手动终止，
     * 那么则只有当系统的垃圾收集被调用的时候才会对其进行回收终止
     */
    public static void main(String[] args) {
        System.out.println("定时任务初始时间："+System.currentTimeMillis());
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行时间："+System.currentTimeMillis());
            }
        },500);

    }

}
